﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using DeepEarth.Core;
using DeepEarth.Map.Core.Tiling;
using DeepEarth.Map.Core.Utilities;
namespace DeepEarth.Map.Core
{
    // When added to a Layer a TileLayer is converted into a multiscale image with a tilesource with the bounding box applied. The purpose
    // of this class is to enable a multiscale image control with a lat long bound tile source to be specified in xaml
    public class TileLayer : UIElementLayer
    {
        const int ClipMax = 32767; // Silverlight object max size

        public TileLayer()
        {
            HorizontalAlignment = HorizontalAlignment.Stretch;
            VerticalAlignment = VerticalAlignment.Stretch;
        }

        #region Properties
        public static readonly DependencyProperty BoundingBoxProperty = DependencyProperty.Register("BoundingBox", typeof(LocationRectangle), typeof(TileLayer), new PropertyMetadata(OnBoundingBoxChanged));
        public LocationRectangle BoundingBox
        {
            get { return (LocationRectangle)GetValue(BoundingBoxProperty); }
            set { SetValue(BoundingBoxProperty, value); }
        }

        static void OnBoundingBoxChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var self = (TileLayer)sender;

            if (args.NewValue != null && self.TileSource != null)
            {
                var lr = args.NewValue as LocationRectangle;
                self.TileSource.LogicalBounds = LocationRectangleToLogicalRect(lr);

                self.SynchronizeBoundingBox();
            }
        }

        public static readonly DependencyProperty TileSourceProperty = DependencyProperty.Register("TileSource", typeof(BaseTileSource), typeof(TileLayer), new PropertyMetadata(OnTileSourceChanged));
        public BaseTileSource TileSource
        {
            get { return (BaseTileSource)GetValue(TileSourceProperty); }
            set { SetValue(TileSourceProperty, value); }
        }

        static void OnTileSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var self = (TileLayer)sender;

            if (args.NewValue != null && self.BoundingBox != null)
            {
                var bts = (BaseTileSource)args.NewValue;
                bts.LogicalBounds = LocationRectangleToLogicalRect(self.BoundingBox);
            }

            if (self.MultiScaleImage == null)
            {
                self.InitializeMsi();
            }
        }

        internal MultiScaleImage MultiScaleImage { get; set; }
        #endregion

        protected override void SynchronizeOnFrame()
        {
            SynchronizeBoundingBox();
            SynchronizeMsi();
        }

        void InitializeMsi()
        {
            Children.Clear();
            MultiScaleImage = new MultiScaleImage { UseSprings = false };

            var tileSourceBinding = new Binding("TileSource") { Source = this, Mode = BindingMode.OneWay };
            MultiScaleImage.SetBinding(MultiScaleImage.SourceProperty, tileSourceBinding);

            Children.Add(MultiScaleImage);
        }

        protected override void SynchronizeOnEnd()
        {
            SynchronizeOnFrame();
        }

        void SynchronizeBoundingBox()
        {
            if (BoundingBox != null && MapInstance != null)
            {
                Point nw = MapInstance.LocationToViewportPoint(BoundingBox.NorthWest);
                Point se = MapInstance.LocationToViewportPoint(BoundingBox.SouthEast);

                var r = new Rect(nw, se);

                Clip = r.Width < ClipMax ? new RectangleGeometry { Rect = r } : null;
            }
        }

        void SynchronizeMsi()
        {
            if (MultiScaleImage != null && MapInstance != null)
            {
                if (MultiScaleImage.ActualWidth != MapInstance.AsFrameworkElement.ActualWidth)
                {
                    MultiScaleImage.Width = MapInstance.AsFrameworkElement.ActualWidth;
                    MultiScaleImage.Height = MapInstance.AsFrameworkElement.ActualHeight;
                }

                MultiScaleImage.ViewportWidth = MapInstance.CurrentViewportLogicalWidth;

                var centerGeographic = MapInstance.CurrentCenter;
                var centerLogical = CoordinateTransformation.GeographicToLogical(centerGeographic);
                var newOrigin = MapHelper.OriginFromLogicalCentreAtWidth(centerLogical, MapInstance, MultiScaleImage);
                MultiScaleImage.ViewportOrigin = newOrigin;
            }
        }

        public static Rect LocationRectangleToLogicalRect(LocationRectangle locationRect)
        {           
            Point nw = CoordinateTransformation.GeographicToLogical(locationRect.NorthWest);
            Point se = CoordinateTransformation.GeographicToLogical(locationRect.SouthEast);

            return new Rect(nw, se);
        }
    }
}